//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

//
//  This example plugin demonstrates how to implement a Maya File Translator.
//

#include <maya/MStatus.h>
#include <maya/MObject.h>
#include <maya/MFnPlugin.h>
#include <maya/MString.h>
#include <maya/MStringArray.h>
#include <maya/MPxFileTranslator.h>
#include <maya/MGlobal.h>
#include <maya/MFnDagNode.h>
#include <maya/MItDag.h>
#include <maya/MObject.h>
#include <maya/MPlug.h>
#include <maya/MItSelectionList.h>
#include <maya/MSelectionList.h>
#include <maya/MIOStream.h> 
#include <maya/MFStream.h>
#include <string.h> 

class LepTranslator : public MPxFileTranslator {
public:
					LepTranslator () {};
	virtual			~LepTranslator () {};
	static void*	creator();

	MStatus			reader ( const MFileObject& file,
							 const MString& optionsString,
							 MPxFileTranslator::FileAccessMode mode);
	MStatus			writer ( const MFileObject& file,
							 const MString& optionsString,
							 MPxFileTranslator::FileAccessMode mode);
	bool			haveReadMethod () const;
	bool			haveWriteMethod () const;
	MString         defaultExtension () const;
	bool            canBeOpened() const;
	MFileKind		identifyFile (	const MFileObject& fileName,
									const char* buffer,
									short size) const;

private:
	void			getPosition( MObject& transform,
								 double& tx, double& ty, double& tz );
	static MString	magic;
};

void* LepTranslator::creator()
{
	return new LepTranslator();
}

// Initialize our magic "number"
MString LepTranslator::magic("<LEP>");

// An LEP file is an ascii whose first line contains the string <LEP>.
// The read does not support comments, and assumes that the each
// subsequent line of the file contains a valid MEL command that can
// be executed via the "executeCommand" method of the MGlobal class.
//
MStatus LepTranslator::reader ( const MFileObject& file,
								const MString& options,
								MPxFileTranslator::FileAccessMode mode)
{
	#if defined (OSMac_)
		char nameBuffer[MAXPATHLEN];
		strcpy (nameBuffer, file.fullName().asChar());
		const MString fname(nameBuffer);
	#else
		const MString fname = file.fullName();
	#endif	

	MStatus rval(MS::kSuccess);
	const int maxLineSize = 1024;
	char buf[maxLineSize];

	ifstream inputfile(fname.asChar(), ios::in);
	if (!inputfile) {
		// open failed
		cerr << fname << ": could not be opened for reading\n";
		return MS::kFailure;
	}

	if (!inputfile.getline (buf, maxLineSize)) {
		cerr << "file " << fname << " contained no lines ... aborting\n";
		return MS::kFailure;
	}

	if (0 != strncmp(buf, magic.asChar(), magic.length())) {
		cerr << "first line of file " << fname;
		cerr << " did not contain " << magic.asChar() << " ... aborting\n";
		return MS::kFailure;
	}

	while (inputfile.getline (buf, maxLineSize)) {
		MString cmdString;

		cmdString.set(buf);
		if (!MGlobal::executeCommand(cmdString))
			rval = MS::kFailure;
	}
	inputfile.close();

	return rval;
}

// Write out the contents of the DAG in LEP format.  This is by no
// means a full implementation.  It only writes out transforms for the
// nurbs primitives listed below, and ignores all other objects.  If
// anything other than the default Maya naming (primitiveName#) is used
// for the primitives, the write routine will not recognise them.

// The currently recognised primitives.
const char* primitiveStrings[] = {
	"nurbsSphere",
	"nurbsCone",
	"nurbsCylinder",
};
const unsigned numPrimitives = sizeof(primitiveStrings) / sizeof(char*);

// Corresponding commands to create the primitives
const char* primitiveCommands[] = {
	"sphere",
	"cone",
	"cylinder",
};

MStatus LepTranslator::writer ( const MFileObject& file,
								const MString& options,
								MPxFileTranslator::FileAccessMode mode)
{
	MStatus status;
	bool showPositions = false;
	unsigned int  i;
	const MString fname = file.fullName();

	ofstream newf(fname.asChar(), ios::out);
	if (!newf) {
		// open failed
		cerr << fname << ": could not be opened for reading\n";
		return MS::kFailure;
	}
	newf.setf(ios::unitbuf);

	if (options.length() > 0) {
		// Start parsing.
		MStringArray optionList;
		MStringArray theOption;
		options.split(';', optionList);	// break out all the options.
		
		for( i = 0; i < optionList.length(); ++i ){
			theOption.clear();
			optionList[i].split( '=', theOption );
			if( theOption[0] == MString("showPositions") &&
													theOption.length() > 1 ) {
				if( theOption[1].asInt() > 0 ){
					showPositions = true;
				}else{
					showPositions = false;
				}
			}
		}
	}

	// output our magic number
	newf << "<LEP>\n";

	MItDag dagIterator( MItDag::kBreadthFirst, MFn::kInvalid, &status);

	if ( !status) {
		status.perror ("Failure in DAG iterator setup");
		return MS::kFailure;
	}

	MSelectionList selection;
	MGlobal::getActiveSelectionList (selection);
	MItSelectionList selIterator (selection, MFn::kDagNode);

	bool done = false;

	while (true) {
		MObject currentNode;
		switch (mode)
		{
			case MPxFileTranslator::kSaveAccessMode:
			case MPxFileTranslator::kExportAccessMode:
				if (dagIterator.isDone ())
					done = true;
				else {
					currentNode = dagIterator.item ();
					dagIterator.next ();
				}
				break;
			case MPxFileTranslator::kExportActiveAccessMode:
				if (selIterator.isDone ())
					done = true;
				else {
					selIterator.getDependNode (currentNode);
					selIterator.next ();
				}
				break;
			default:
				cerr << "Unrecognized write mode: " << mode << endl;
				break;
		}
		if (done)
			break;

		MFnDagNode dagNode(currentNode, &status);
		if ( !status ) {
			status.perror ("Attaching MFnDagNode");
			continue;
		}

		MString nodeName   = dagNode.name();
		MFn::Type nodeType = currentNode.apiType();
		MStringArray primitives(primitiveStrings, numPrimitives);
		MStringArray commands(primitiveCommands, numPrimitives);

		if (nodeType == MFn::kTransform) {
			// We are only dealing with transforms
			// And actually, only the transforms with names that
			// match the primitive names specified above.
			for (i = 0; i < primitives.length(); ++i) {
				unsigned len = primitives[i].length();
				if (nodeName.length() < len)
					continue; // Can't possibly match
				if (primitives[i] == nodeName.substring(0, len - 1)) {
					// This is a node we support
					newf << commands[i] << " -n " << nodeName << endl;

					if (showPositions) {
						double tx, ty, tz;
						getPosition(currentNode, tx, ty, tz);
						newf << "move " << tx << " " << ty << " " << tz << endl;
					}
				}
			}
			    
		}
	}

	newf.close();
	return MS::kSuccess;
}

bool LepTranslator::haveReadMethod () const
{
	return true;
}

bool LepTranslator::haveWriteMethod () const
{
	return true;
}

// Whenever Maya needs to know the preferred extension of this file format,
// it calls this method. For example, if the user tries to save a file called
// "test" using the Save As dialog, Maya will call this method and actually
// save it as "test.lep". Note that the period should *not* be included in
// the extension.
MString LepTranslator::defaultExtension () const
{
	return "lep";
}

// This method tells Maya whether the translator can open and import files
// (returns true) or only import files (returns false)
bool LepTranslator::canBeOpened () const
{
	return true;
}

MPxFileTranslator::MFileKind LepTranslator::identifyFile (
										const MFileObject& fileName,
										const char* buffer,
										short size) const
{
	// Check the buffer for the "LEP" magic number, the
	// string "<LEP>\n"

	MFileKind rval = kNotMyFileType;

	if ((size >= (short)magic.length()) &&
		(0 == strncmp(buffer, magic.asChar(), magic.length()))) {

		/*
		cout << "LepTranslator::identifyFile\n";
		cout << "\tname: \"" << fileName.name() << "\"\n";
		cout << "\tpath: \"" << fileName.path() << "\"\n";
		cout << "\trawPath: \"" << fileName.rawPath() << "\"\n";
		cout << "\tfullName: \"" << fileName.fullName() << "\"\n";
		cout << "\tsize: " << size << endl;
		*/

		rval = kIsMyFileType;
	}
	return rval;
}

void LepTranslator::getPosition( MObject& transform,
								 double& tx, double& ty, double& tz )
{
	MObject attr;
	MFnDependencyNode dgNode(transform);

	tx = ty = tz = 0.0;

	// Get plugs to the x, y, and z position attributes of the transform
	attr = dgNode.attribute( MString("translateX"));
	MPlug txPlug( transform, attr );

	attr = dgNode.attribute( MString("translateY"));
	MPlug tyPlug( transform, attr );

	attr = dgNode.attribute( MString("translateZ"));
	MPlug tzPlug( transform, attr );

	// Extract the position data from this transform
	txPlug.getValue( tx );
	tzPlug.getValue( tz );
	tyPlug.getValue( ty );
}

MStatus initializePlugin( MObject obj )
{
	MStatus   status;
	MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

	// Register the translator with the system
	status =  plugin.registerFileTranslator( "Lep",
										"lepTranslator.rgb",
										LepTranslator::creator,
										"lepTranslatorOpts",
										"showPositions=1",
										true );
	if (!status) {
		status.perror("registerFileTranslator");
		return status;
	}

	return status;
}

MStatus uninitializePlugin( MObject obj )
{
	MStatus   status;
	MFnPlugin plugin( obj );

	status =  plugin.deregisterFileTranslator( "Lep" );
	if (!status) {
		status.perror("deregisterFileTranslator");
		return status;
	}

	return status;
}
